# -*- coding: UTF-8 -*-
"""
@Project ：swift-forge-engine 
@File    ：menu_service.py
@IDE     ：PyCharm 
@Author  ：Tony.Gan
@Date    ：2025/2/24 1:14 
@Des     ：
"""
from typing import Any

from fastapi import Request

from App.app.admin.crud.menu_crud import MenuDao
from App.app.admin.crud.sys_role_crud import SysRoleDao
from App.app.admin.models.sys_menu import SysMenu
from App.app.admin.schemas.menu_schema import CreateMenuParam, UpdateMenuParam
from App.common.exception.errors import CustomError, NotFoundError
from App.config.conf import settings
from App.database.mysql import async_db_session
from App.database.redis import redis_client
from App.utils.build_tree import get_tree_data, convert_buttons_to_auth_list


class MenuServices:
    @staticmethod
    async def get(*, pk: int) -> SysMenu:
        async with async_db_session() as db:
            menu = await MenuDao.get(db, pk=pk)
            if not menu:
                raise NotFoundError(message='菜单不存在')
            return menu

    @staticmethod
    async def get_menu_tree(*, name: str | None = None, status: int | None = None) -> list[dict[str, Any]]:
        async with async_db_session() as db:
            menu_select = await MenuDao.get_all(db, name=name, status=status)
            menu_tree = get_tree_data(menu_select)
            return menu_tree

    @staticmethod
    async def get_role_menu(*, pk: int) -> list[dict[str, Any]]:
        async with async_db_session() as db:
            role = await SysRoleDao.get_with_relation(db, pk)
            if not role:
                raise NotFoundError(message='角色不存在')
            menu_ids = [menu.id for menu in role.menus]
            return menu_ids

    @staticmethod
    async def get_user_menu_tree(*, request: Request) -> list[dict[str, Any]]:
        async with async_db_session() as db:
            superuser = request.user.is_super
            role = await SysRoleDao.get_with_relation(db, role_id=int(request.user.role_id))
            menu_ids = []
            if role:
                # 需要循环遍历检查每个菜单的父ID是否在列表内，如果不在的话，需要将其添加进列表中
                existing = set(menu_ids)
                to_add = []
                for menu in role.menus:
                    # 添加菜单ID
                    if menu.id not in existing:
                        to_add.append(menu.id)
                        existing.add(menu.id)

                    # 检查并添加父级ID
                    if menu.parent_id is not None and menu.parent_id not in existing:
                        to_add.append(menu.parent_id)
                        existing.add(menu.parent_id)

                menu_ids.extend(to_add)
            menu_select = await MenuDao.get_role_menus(db, superuser, menu_ids)
            menu_tree = get_tree_data(menu_select)
            # 执行转换
            converted_tree = convert_buttons_to_auth_list(menu_tree)
            return converted_tree

    @staticmethod
    async def create(*, obj_in: CreateMenuParam) -> None:
        async with async_db_session.begin() as db:
            menu = await MenuDao.get_by_name(db, obj_in.name)
            if menu:
                raise CustomError(message='菜单名称已存在')
            if obj_in.parent_id:
                parent_menu = await MenuDao.get(db, obj_in.parent_id)
                if not parent_menu:
                    raise NotFoundError(message='父级菜单不存在')
            await MenuDao.create(db, obj_in)

    @staticmethod
    async def update(*, obj_in: UpdateMenuParam) -> int:
        async with async_db_session.begin() as db:
            menu = await MenuDao.get(db, obj_in.id)
            if not menu:
                raise NotFoundError(message='菜单不存在')
            if obj_in.parent_id:
                parent_menu = await MenuDao.get(db, obj_in.parent_id)
                if not parent_menu:
                    raise NotFoundError(message='父级菜单不存在')
            if obj_in.parent_id == menu.id:
                raise CustomError(message='禁止关联自身为父级')
            count = await MenuDao.update(db, obj_in.id, obj_in)
            return count

    @staticmethod
    async def delete(*, request: Request, pk: int) -> int:
        async with async_db_session.begin() as db:
            children = await MenuDao.get_children(db, pk)
            if children:
                raise CustomError(message='菜单下存在子菜单，无法删除')
            count = await MenuDao.delete(db, pk)
            await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{request.user.id}')
            return count


MenuService: MenuServices = MenuServices()
